<!--
 * @Author: Dorange.Wang
 * @Date: 2021-05-20 09:49:15
 * @LastEditors: your name
 * @LastEditTime: 2021-05-25 15:24:00
 * @Description: file content
-->
<template>
  <div>
    <el-radio-group v-model="formData.bodyType">
      <el-radio
        v-for="(value, key) in REQUEST_BODY_TYPE_ENUM"
        :key="key"
        :label="key"
        >{{ value }}</el-radio
      >
    </el-radio-group>

    <template v-if="formData.bodyType === 'RAW'">
      <el-button
        class="header-btn"
        style="margin-top: 10px"
        type="text"
        @click="handleJsonAdd"
        >{{ jsonData ? "编辑结构体" : "添加结构体" }}</el-button
      >

      <div v-if="formData.bodyType === 'RAW'">
        <JsonView
          v-if="jsonData"
          :data="jsonData"
          :closed="false"
          :showValue="false"
          :line-height="40"
          :deep="5"
          :showQuote="false"
        >
          <template slot="suffix" slot-scope="scope">
            <el-input
              class="input-with-select"
              style="width: 500px"
              :value="JSON.parse(scope.data.value).value"
              @input="handleValueChange($event, scope.data.value)"
            >
              <el-select
                style="width: 100px"
                slot="prepend"
                placeholder="请选择"
                :value="JSON.parse(scope.data.value).valueType"
                @change="handleValueTypeChange($event, scope.data.value)"
              >
                <el-option value="BUILT_IN" label="内置" />
                <el-option value="CUSTOM" label="自定义" />
              </el-select>
            </el-input>
          </template>
          <i class="el-icon-circle-plus-outline" slot="closedIcon" />
          <i class="el-icon-remove-outline" slot="openedIcon" />
        </JsonView>

        <div class="json-data-error">{{ jsonDataError }}</div>
      </div>
    </template>

    <HttpReqTable
      class="mt10"
      v-if="['FORM_DATA', 'X_WWW_FORM_URLENCODED'].includes(formData.bodyType)"
      ref="HttpReqTable"
    />
  </div>
</template>

<script>
import lodash from "lodash";
import { REQUEST_BODY_TYPE_ENUM } from "@/utils/enum";
import { VARIABLE_BUILT_IN_REG } from "@/utils/reg";
import ScriptEditor from "@/components/ScriptEditor";
import HttpReqTable from "./HttpReqTable";
import JsonView from "@/components/JsonView";
import jsonFormat from "json-format";

const defaultBodyType = Object.keys(REQUEST_BODY_TYPE_ENUM)[0];

export default {
  name: "HttpReqBody",

  components: {
    HttpReqTable,
    JsonView,
  },

  data() {
    return {
      formData: {
        bodyType: defaultBodyType,
      },
      REQUEST_BODY_TYPE_ENUM,
      content: undefined,
      jsonData: undefined,
      jsonDataError: "",
    };
  },

  methods: {
    jsonTrans(data, path = "") {
      const type = this.getDataType(data);
      if (type === "array") {
        return data.map((item, index) => {
          const itemPath = path ? `${path}[${index}]` : `[${index}]`;
          return this.jsonTrans(item, itemPath);
        });
      } else if (type === "object") {
        const obj = {};
        Object.keys(data).forEach((key) => {
          const value = data[key];
          const keyPath = path ? `${path}.${key}` : `${key}`;
          obj[key] = this.jsonTrans(value, keyPath);
        });
        return obj;
      } else {
        const valueObj = {
          value: String(data),
          valuePath: path,
          valueType:
            type === "string" && VARIABLE_BUILT_IN_REG.test(data)
              ? "BUILT_IN"
              : "CUSTOM",
        };
        return JSON.stringify(valueObj);
      }
    },

    getDataType(data) {
      const type = Object.prototype.toString
        .call(data)
        .slice(8, -1)
        .toLowerCase();
      if (type === "number") {
        if (data.toString().includes(".")) {
          return "double";
        } else {
          return "int";
        }
      }
      return type;
    },

    setData(data) {
      const { body, bodyType } = data;
      this.formData.bodyType = bodyType || defaultBodyType;
      this.$nextTick(() => {
        if (["FORM_DATA", "X_WWW_FORM_URLENCODED"].includes(bodyType)) {
          this.$refs.HttpReqTable.setData(body);
        } else if (bodyType === "RAW") {
          this.initJsonData(body);
        }
      });
    },

    initJsonData(data) {
      this.content = typeof data === "string" ? JSON.parse(data) : data;
      this.jsonData = this.jsonTrans(lodash.cloneDeep(this.content));
    },

    handleValueTypeChange(evt, value) {
      const valueObj = JSON.parse(value);
      const newValObj = {
        ...valueObj,
        valueType: evt,
      };
      this.$set(this.jsonData, valueObj.valuePath, JSON.stringify(newValObj));
    },

    handleValueChange(evt, value) {
      const valueObj = JSON.parse(value);
      const newValObj = {
        ...valueObj,
        value: evt,
      };
      this.$set(this.jsonData, valueObj.valuePath, JSON.stringify(newValObj));
      this.$set(this.content, valueObj.valuePath, evt);
    },

    handleJsonAdd() {
      ScriptEditor.open(
        {
          title: "添加结构体",
          language: "json",
          autoClose: false,
          content: this.content
            ? jsonFormat(this.content, { type: "space", size: 4 })
            : "",
        },
        (data) => {
          const { content } = data;
          let jsonData;
          try {
            jsonData = JSON.parse(content);
          } catch (err) {
            this.$message.error("语法错误");
            return false;
          }
          this.initJsonData(jsonData);
          ScriptEditor.close();
        }
      );
    },

    validateJson(data) {
      try {
        Object.keys(data).forEach((key) => {
          const value = data[key];
          const type = this.getDataType(value);
          if (type === "array" || type === "object") {
            this.validateJson(value);
          } else if (type === "string") {
            const { value: valueStr, valueType, valuePath } = JSON.parse(value);
            if (!(valueStr || "").trim()) {
              throw new Error(`结构体中“${valuePath}”的值不能为空`);
            } else if (
              valueType === "BUILT_IN" &&
              !VARIABLE_BUILT_IN_REG.test(valueStr)
            ) {
              throw new Error(`结构体中“${valuePath}”的值与类型不匹配`);
            }
          }
        });
      } catch (err) {
        this.jsonDataError = err.message;
      }
    },

    async getData() {
      const { bodyType } = this.formData;
      if (bodyType === "NONE") {
        return {
          bodyType,
          body: "",
        };
      }

      if (["FORM_DATA", "X_WWW_FORM_URLENCODED"].includes(bodyType)) {
        const params = await this.$refs.HttpReqTable.getData();
        if (params === false) {
          return false;
        }
        return {
          bodyType,
          body: params,
        };
      }

      if (bodyType === "RAW") {
        this.jsonDataError = "";
        this.validateJson(this.jsonData);
        if (this.jsonDataError) {
          return false;
        }
        return {
          bodyType,
          body: this.content ? JSON.stringify(this.content) : "",
        };
      }

      return false;
    },
  },
};
</script>

<style lang="scss" scoped>
.el-table ::v-deep .el-form-item {
  margin-bottom: 0;
}

.el-radio-group {
  height: 18px;
  display: flex;
  align-items: center;
}

.header-btn.el-button--text {
  padding: 0;
  height: 18px;
  display: flex;
  align-items: center;
}

.input-with-select ::v-deep .el-input-group__prepend {
  background-color: #fff;
}

.json-data-error {
  color: #f56c6c;
  font-size: 12px;
  line-height: 1;
}
</style>
